<template><div><h2 id="了解html的约束规范与发展历史" tabindex="-1"><a class="header-anchor" href="#了解html的约束规范与发展历史"><span>了解HTML的约束规范与发展历史</span></a></h2>
<p>&quot;&lt;!DOCTYPE&gt;&quot;标签有什么用？DTD是什么？HTML每一次演进都有哪些变化，HTML5相较于之前的版本有什么优化？</p>
<p>DOCTYPE document type 声明文档类型  浏览器会根据不同的文档类型进行对应的渲染</p>
<p>浏览器渲染存在两种模式，标准模式和混杂模式，使用标准模式会严格按照最新的html规范，使用混杂模式则会模拟旧版浏览器渲染实现向后兼容</p>
<p>DTD</p>
<p>document type define 文档类型定义，实则是一组规则,DOCTYPE声明包含了指向DTD的URL，浏览器通过这个URL获取DTD信息，以确定如何解析HTML文档。</p>
<ul>
<li>html5文档声明</li>
</ul>
<div class="language-html line-numbers-mode" data-highlighter="shiki" data-ext="html" style="background-color:#282c34;color:#abb2bf"><pre class="shiki one-dark-pro vp-code" v-pre=""><code><span class="line"><span style="color:#ABB2BF">&#x3C;!</span><span style="color:#E06C75">DOCTYPE</span><span style="color:#D19A66"> html</span><span style="color:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><ul>
<li>html4.01文档声明</li>
</ul>
<div class="language-html line-numbers-mode" data-highlighter="shiki" data-ext="html" style="background-color:#282c34;color:#abb2bf"><pre class="shiki one-dark-pro vp-code" v-pre=""><code><span class="line"><span style="color:#ABB2BF">&#x3C;!</span><span style="color:#E06C75">DOCTYPE</span><span style="color:#D19A66"> HTML</span><span style="color:#D19A66"> PUBLIC</span><span style="color:#98C379"> "-//W3C//DTD HTML 4.01//EN"</span><span style="color:#98C379"> "http://www.w3.org/TR/html4/strict.dtd"</span><span style="color:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><blockquote>
<p>HTML每一次演进都有哪些变化，HTML5相较于之前的版本有什么优化？</p>
</blockquote>
<blockquote>
<p>参考：<a href="https://blog.csdn.net/u011355389/article/details/144446789" target="_blank" rel="noopener noreferrer">DOCTYPE的作用是什么？-CSDN博客</a></p>
</blockquote>
<h2 id="学习html的渲染过程-思考vue的渲染流程" tabindex="-1"><a class="header-anchor" href="#学习html的渲染过程-思考vue的渲染流程"><span>学习HTML的渲染过程，思考VUE的渲染流程</span></a></h2>
<p>HTML标签到DOM树再到浏览器界面的渲染流程是什么？DOM更新的流程是怎样的？会触发浏览器界面重绘的操作有哪些？开销从大到小排个序。</p>
<p>浏览器的渲染流程</p>
<ol>
<li>解析HTML构建DOM树</li>
<li>解析CSS构建CSSOM树</li>
<li>合并DOM树和CSSOM树构建RENDER树（渲染树）</li>
<li>生成布局FLOW，即将所有RENDER树的所有节点进行平面合成</li>
<li>浏览器进行绘制</li>
</ol>
<p>DOM的更新流程</p>
<ol>
<li>DOM树更新</li>
<li>根据DOM树的修改，重新计算节点的样式，更新或者构建新的CSSOM树</li>
<li>对应更新RENDER树</li>
<li>浏览器进行重新绘制</li>
</ol>
<blockquote>
<p>参考：<a href="https://geekdaxue.co/read/nardo@goi5e0/mof8p7#1kgcm7" target="_blank" rel="noopener noreferrer">浏览器技术原理 - 浏览器重绘(repaint)重排(reflow)与优化 - 《📚 技术修行》 - 极客文档</a></p>
</blockquote>
<p>导致浏览器重绘的操作（从大到小排序）</p>
<ol>
<li>修改元素的几何信息（重排+重绘）</li>
<li>修改元素的位置信息（重排+重绘）</li>
<li>修改元素的外观（重绘）</li>
<li>修改元素的字体（重绘）</li>
<li>修改元素的可见性（重绘）</li>
</ol>
<blockquote>
<p>VUE的渲染流程目前个人理解就是封装了一系列的js操作DOM</p>
</blockquote>
<h2 id="dom的属性与查找" tabindex="-1"><a class="header-anchor" href="#dom的属性与查找"><span>DOM的属性与查找</span></a></h2>
<h3 id="html标签的常用属性有哪些-整理出来" tabindex="-1"><a class="header-anchor" href="#html标签的常用属性有哪些-整理出来"><span>html标签的常用属性有哪些，整理出来</span></a></h3>
<ol>
<li>id html元素的id</li>
<li>class html元素的css类</li>
<li>style html元素的内联样式</li>
<li>title html元素的额外提示信息，通常在鼠标悬停时显示</li>
</ol>
<h3 id="dom元素的查找方法有哪些-分别举例。" tabindex="-1"><a class="header-anchor" href="#dom元素的查找方法有哪些-分别举例。"><span>dom元素的查找方法有哪些？分别举例。</span></a></h3>
<div class="language-js line-numbers-mode" data-highlighter="shiki" data-ext="js" style="background-color:#282c34;color:#abb2bf"><pre class="shiki one-dark-pro vp-code" v-pre=""><code><span class="line"><span style="color:#C678DD">let</span><span style="color:#E06C75"> element</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> document</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementById</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'e'</span><span style="color:#ABB2BF">); </span><span style="color:#7F848E;font-style:italic">// 通过id查找</span></span>
<span class="line"><span style="color:#C678DD">let</span><span style="color:#E06C75"> element</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> document</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementsByClassName</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'e'</span><span style="color:#ABB2BF">); </span><span style="color:#7F848E;font-style:italic">// 通过类名查找</span></span>
<span class="line"><span style="color:#C678DD">let</span><span style="color:#E06C75"> element</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> document</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementsByTagName</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'e'</span><span style="color:#ABB2BF">); </span><span style="color:#7F848E;font-style:italic">// 通过标签名查找</span></span>
<span class="line"><span style="color:#C678DD">let</span><span style="color:#E06C75"> element</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> document</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">querySelector</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'.e'</span><span style="color:#ABB2BF">); </span><span style="color:#7F848E;font-style:italic">// 通过css选择器查找</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="html标签的属性可以自定义吗-如何读取与修改。" tabindex="-1"><a class="header-anchor" href="#html标签的属性可以自定义吗-如何读取与修改。"><span>html标签的属性可以自定义吗，如何读取与修改。</span></a></h3>
<p>html5可以使用data-*来自定义属性</p>
<div class="language-javascript line-numbers-mode" data-highlighter="shiki" data-ext="javascript" style="background-color:#282c34;color:#abb2bf"><pre class="shiki one-dark-pro vp-code" v-pre=""><code><span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> divElement</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> document</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">querySelector</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'div'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B">console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">log</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">divElement</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">dataset</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">userId</span><span style="color:#ABB2BF">); </span><span style="color:#7F848E;font-style:italic">// 读取自定义属性</span></span>
<span class="line"><span style="color:#E5C07B">divElement</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">dataset</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">userId</span><span style="color:#56B6C2"> =</span><span style="color:#98C379"> '456'</span><span style="color:#ABB2BF">; </span><span style="color:#7F848E;font-style:italic">// 修改自定义属性</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>对于非data-*的自定义属性，可以使用getAttribute和setAttribute方法来读取和修改。</p>
<div class="language-javascript line-numbers-mode" data-highlighter="shiki" data-ext="javascript" style="background-color:#282c34;color:#abb2bf"><pre class="shiki one-dark-pro vp-code" v-pre=""><code><span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> divElement</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> document</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">querySelector</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'div'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B">console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">log</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">divElement</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getAttribute</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'custom-attribute'</span><span style="color:#ABB2BF">)); </span><span style="color:#7F848E;font-style:italic">// 读取属性</span></span>
<span class="line"><span style="color:#E5C07B">divElement</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">setAttribute</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'custom-attribute'</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'new-value'</span><span style="color:#ABB2BF">); </span><span style="color:#7F848E;font-style:italic">// 设置属性</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="练习" tabindex="-1"><a class="header-anchor" href="#练习"><span>练习</span></a></h3>
<p>html标签是树形结构的,渲染成DOM也是树形结构的，JSON也是树形结构的[手动狗头]</p>
<p>提示：</p>
<p>html标签 = json object对象
html属性可以表达为json 对象的属性
html标签嵌套可以表达为children数组
要求：写一个js函数，将DOM树转化为一个json对象，要求保留dom树的全部信息</p>
<p>思考：什么TM是各种前端MVVM框架的虚拟DOM树</p>
<div class="language-html line-numbers-mode has-collapsed-lines collapsed" data-highlighter="shiki" data-ext="html" style="--vp-collapsed-lines:15;background-color:#282c34;color:#abb2bf"><pre class="shiki one-dark-pro vp-code" v-pre=""><code><span class="line"><span style="color:#ABB2BF">&#x3C;!</span><span style="color:#E06C75">DOCTYPE</span><span style="color:#D19A66"> html</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">&#x3C;</span><span style="color:#E06C75">html</span><span style="color:#D19A66"> lang</span><span style="color:#ABB2BF">=</span><span style="color:#98C379">"en"</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">&#x3C;</span><span style="color:#E06C75">head</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">    &#x3C;</span><span style="color:#E06C75">meta</span><span style="color:#D19A66"> charset</span><span style="color:#ABB2BF">=</span><span style="color:#98C379">"UTF-8"</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">    &#x3C;</span><span style="color:#E06C75">meta</span><span style="color:#D19A66"> name</span><span style="color:#ABB2BF">=</span><span style="color:#98C379">"viewport"</span><span style="color:#D19A66"> content</span><span style="color:#ABB2BF">=</span><span style="color:#98C379">"width=device-width, initial-scale=1.0"</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">    &#x3C;</span><span style="color:#E06C75">title</span><span style="color:#ABB2BF">>DOM转JSON&#x3C;/</span><span style="color:#E06C75">title</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">&#x3C;/</span><span style="color:#E06C75">head</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">&#x3C;</span><span style="color:#E06C75">body</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">    &#x3C;</span><span style="color:#E06C75">div</span><span style="color:#D19A66"> id</span><span style="color:#ABB2BF">=</span><span style="color:#98C379">"app"</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">        &#x3C;</span><span style="color:#E06C75">h1</span><span style="color:#ABB2BF">>test-h1&#x3C;/</span><span style="color:#E06C75">h1</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">        &#x3C;</span><span style="color:#E06C75">p</span><span style="color:#ABB2BF">>test-p&#x3C;/</span><span style="color:#E06C75">p</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">        &#x3C;</span><span style="color:#E06C75">span</span><span style="color:#ABB2BF">>test-span&#x3C;/</span><span style="color:#E06C75">span</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">        &#x3C;</span><span style="color:#E06C75">div</span><span style="color:#ABB2BF">>test-div&#x3C;</span><span style="color:#E06C75">p</span><span style="color:#D19A66"> class</span><span style="color:#ABB2BF">=</span><span style="color:#98C379">"test-class"</span><span style="color:#ABB2BF">>test-p&#x3C;/</span><span style="color:#E06C75">p</span><span style="color:#ABB2BF">>&#x3C;/</span><span style="color:#E06C75">div</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">    &#x3C;/</span><span style="color:#E06C75">div</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">    &#x3C;</span><span style="color:#E06C75">script</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#C678DD">        function</span><span style="color:#61AFEF"> domToJSON</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">element</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD">            if</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">element</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">nodeType</span><span style="color:#56B6C2"> ===</span><span style="color:#D19A66"> 3</span><span style="color:#ABB2BF">)  </span><span style="color:#C678DD">return</span><span style="color:#ABB2BF"> { </span><span style="color:#E06C75">text</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">element</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">textContent</span><span style="color:#ABB2BF"> } </span><span style="color:#7F848E;font-style:italic">// 处理文本内容</span></span>
<span class="line"><span style="color:#C678DD">            let</span><span style="color:#E06C75"> attributes</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> element</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getAttributeNames</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#E06C75">            attributeObject</span><span style="color:#56B6C2"> =</span><span style="color:#ABB2BF"> {};</span></span>
<span class="line"><span style="color:#C678DD">            for</span><span style="color:#ABB2BF">(</span><span style="color:#C678DD">let</span><span style="color:#E06C75"> i</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> 0</span><span style="color:#ABB2BF">; </span><span style="color:#E06C75">i</span><span style="color:#56B6C2"> &#x3C;</span><span style="color:#E5C07B"> attributes</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">length</span><span style="color:#ABB2BF">; </span><span style="color:#E06C75">i</span><span style="color:#56B6C2">++</span><span style="color:#ABB2BF">) </span></span>
<span class="line"><span style="color:#E06C75">                attributeObject</span><span style="color:#ABB2BF">[</span><span style="color:#E06C75">attributes</span><span style="color:#ABB2BF">[</span><span style="color:#E06C75">i</span><span style="color:#ABB2BF">]] </span><span style="color:#56B6C2">=</span><span style="color:#E5C07B"> element</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getAttribute</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">attributes</span><span style="color:#ABB2BF">[</span><span style="color:#E06C75">i</span><span style="color:#ABB2BF">]);</span></span>
<span class="line"><span style="color:#C678DD">            let</span><span style="color:#E06C75"> children</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> element</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">childNodes</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD">            let</span><span style="color:#E06C75"> childrenArray</span><span style="color:#56B6C2"> =</span><span style="color:#ABB2BF"> [];</span></span>
<span class="line"><span style="color:#C678DD">            for</span><span style="color:#ABB2BF">(</span><span style="color:#C678DD">let</span><span style="color:#E06C75"> i</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> 0</span><span style="color:#ABB2BF">; </span><span style="color:#E06C75">i</span><span style="color:#56B6C2"> &#x3C;</span><span style="color:#E5C07B"> children</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">length</span><span style="color:#ABB2BF">; </span><span style="color:#E06C75">i</span><span style="color:#56B6C2">++</span><span style="color:#ABB2BF">)  </span></span>
<span class="line"><span style="color:#E5C07B">                childrenArray</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">push</span><span style="color:#ABB2BF">(</span><span style="color:#61AFEF">domToJSON</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">children</span><span style="color:#ABB2BF">[</span><span style="color:#E06C75">i</span><span style="color:#ABB2BF">]));</span></span>
<span class="line"><span style="color:#C678DD">            return</span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75">                tagName</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">element</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">tagName</span><span style="color:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75">                attributes</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">attributeObject</span><span style="color:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75">                children</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">childrenArray</span></span>
<span class="line"><span style="color:#ABB2BF">            };</span></span>
<span class="line"><span style="color:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#C678DD">        const</span><span style="color:#E5C07B"> json</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> domToJSON</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">document</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementById</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">"app"</span><span style="color:#ABB2BF">));</span></span>
<span class="line"><span style="color:#E5C07B">        console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">log</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">json</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">    &#x3C;/</span><span style="color:#E06C75">script</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">&#x3C;/</span><span style="color:#E06C75">body</span><span style="color:#ABB2BF">></span></span>
<span class="line"><span style="color:#ABB2BF">&#x3C;/</span><span style="color:#E06C75">html</span><span style="color:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div><div class="collapsed-lines"></div></div><blockquote>
<p>为什么要引入虚拟DOM树？</p>
<p>因为js操作一次DOM性能开销很大，涉及到多次操作DOM时，可以在虚拟DOM树进行操作，然后合成多次操作在操纵DOM，从多次操纵DOM的性能开销，降低为操纵一次DOM的性能开销</p>
</blockquote>
<blockquote>
<p>手搓了一个小vue框架:  <a href="https://gitee.com/haomingze/mini-vue" target="_blank" rel="noopener noreferrer">mini-vue: 一款类vue框架</a></p>
<p>参考：</p>
<p><a href="https://blog.csdn.net/w18758879921/article/details/128974601" target="_blank" rel="noopener noreferrer">js 函数.call调用和直接调用的区别_js 中的直接.call 会快些?-CSDN博客</a></p>
<p><a href="https://blog.csdn.net/qq_39620552/article/details/144651448?spm=1001.2014.3001.5502" target="_blank" rel="noopener noreferrer">JS中的原型与原型链-CSDN博客</a></p>
<p><a href="https://blog.csdn.net/qq_39620552/article/details/145441586" target="_blank" rel="noopener noreferrer">手写MVVM框架-构建虚拟dom树_手写虚拟dom构建-CSDN博客</a></p>
<p><a href="https://vue3js.cn/interview/vue/vnode.html" target="_blank" rel="noopener noreferrer">面试官：什么是虚拟DOM？如何实现一个虚拟DOM？说说你的思路 | web前端面试 - 面试官系列</a></p>
</blockquote>
<h2 id="如何自定义html标签" tabindex="-1"><a class="header-anchor" href="#如何自定义html标签"><span>如何自定义HTML标签</span></a></h2>
<p>学习如何使用纯Html+JS自定义HTML标签，思考并整理各开发框架的组件是如何实现的
练习：使用HTML+JS的方式定义一个功能齐全的登录框标签出来</p>
<p>要求：
a.有用户名，密码输入框，与登录按钮，点击登录按钮可以调用接口
b.通过自定义属性，可从外部赋值用户名与密码的数据字段与接口url地址
c.通过事件冒泡，监听按钮被点击，从父级元素的事件处理上删除该登录框标签</p>
<p>index.js（组件声明）</p>
<div class="language-javascript line-numbers-mode has-collapsed-lines collapsed" data-highlighter="shiki" data-ext="javascript" style="--vp-collapsed-lines:15;background-color:#282c34;color:#abb2bf"><pre class="shiki one-dark-pro vp-code" v-pre=""><code><span class="line"><span style="color:#C678DD">export</span><span style="color:#C678DD"> default</span><span style="color:#C678DD"> class</span><span style="color:#E5C07B"> LoginFrame</span><span style="color:#C678DD"> extends</span><span style="color:#E5C07B"> HTMLElement</span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD">    constructor</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;font-style:italic">        super</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD">        const</span><span style="color:#E5C07B"> shadowRoot</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">attachShadow</span><span style="color:#ABB2BF">({</span></span>
<span class="line"><span style="color:#E06C75">            mode</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">'open'</span><span style="color:#ABB2BF"> </span></span>
<span class="line"><span style="color:#ABB2BF">        });</span></span>
<span class="line"><span style="color:#E5C07B">        shadowRoot</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">innerHTML</span><span style="color:#56B6C2"> =</span><span style="color:#98C379"> `</span></span>
<span class="line"><span style="color:#98C379">            &#x3C;style></span></span>
<span class="line"><span style="color:#98C379">                .login-container {</span></span>
<span class="line"><span style="color:#98C379">                    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;</span></span>
<span class="line"><span style="color:#98C379">                    background-color: #fff;</span></span>
<span class="line"><span style="color:#98C379">                    border-radius: 8px;</span></span>
<span class="line"><span style="color:#98C379">                    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);</span></span>
<span class="line"><span style="color:#98C379">                    padding: 30px;</span></span>
<span class="line"><span style="color:#98C379">                    width: 300px;</span></span>
<span class="line"><span style="color:#98C379">                    margin: auto;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#98C379">                .login-title {</span></span>
<span class="line"><span style="color:#98C379">                    text-align: center;</span></span>
<span class="line"><span style="color:#98C379">                    color: #333;</span></span>
<span class="line"><span style="color:#98C379">                    margin-bottom: 20px;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#98C379">                .form-group {</span></span>
<span class="line"><span style="color:#98C379">                    margin-bottom: 20px;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#98C379">                .form-label {</span></span>
<span class="line"><span style="color:#98C379">                    display: block;</span></span>
<span class="line"><span style="color:#98C379">                    margin-bottom: 5px;</span></span>
<span class="line"><span style="color:#98C379">                    color: #666;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#98C379">                .form-input {</span></span>
<span class="line"><span style="color:#98C379">                    width: 100%;</span></span>
<span class="line"><span style="color:#98C379">                    padding: 10px;</span></span>
<span class="line"><span style="color:#98C379">                    border: 1px solid #ccc;</span></span>
<span class="line"><span style="color:#98C379">                    border-radius: 4px;</span></span>
<span class="line"><span style="color:#98C379">                    box-sizing: border-box;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#98C379">                .error-message {</span></span>
<span class="line"><span style="color:#98C379">                    color: #ff0000;</span></span>
<span class="line"><span style="color:#98C379">                    font-size: 0.9em;</span></span>
<span class="line"><span style="color:#98C379">                    display: none;</span></span>
<span class="line"><span style="color:#98C379">                    margin-top: 5px;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#98C379">                .login-button {</span></span>
<span class="line"><span style="color:#98C379">                    width: 100%;</span></span>
<span class="line"><span style="color:#98C379">                    padding: 10px;</span></span>
<span class="line"><span style="color:#98C379">                    background-color: #007bff;</span></span>
<span class="line"><span style="color:#98C379">                    color: #fff;</span></span>
<span class="line"><span style="color:#98C379">                    border: none;</span></span>
<span class="line"><span style="color:#98C379">                    border-radius: 4px;</span></span>
<span class="line"><span style="color:#98C379">                    cursor: pointer;</span></span>
<span class="line"><span style="color:#98C379">                    transition: background-color 0.3s ease;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#98C379">                .login-button:hover {</span></span>
<span class="line"><span style="color:#98C379">                    background-color: #0056b3;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#98C379">                .result-message {</span></span>
<span class="line"><span style="color:#98C379">                    text-align: center;</span></span>
<span class="line"><span style="color:#98C379">                    margin-top: 20px;</span></span>
<span class="line"><span style="color:#98C379">                    padding: 10px;</span></span>
<span class="line"><span style="color:#98C379">                    border-radius: 4px;</span></span>
<span class="line"><span style="color:#98C379">                    display: none;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#98C379">                .success-message {</span></span>
<span class="line"><span style="color:#98C379">                    background-color: #d4edda;</span></span>
<span class="line"><span style="color:#98C379">                    color: #155724;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#98C379">                .error-message-display {</span></span>
<span class="line"><span style="color:#98C379">                    background-color: #f8d7da;</span></span>
<span class="line"><span style="color:#98C379">                    color: #721c24;</span></span>
<span class="line"><span style="color:#98C379">                }</span></span>
<span class="line"><span style="color:#98C379">            &#x3C;/style></span></span>
<span class="line"><span style="color:#98C379">            &#x3C;div class="login-container"></span></span>
<span class="line"><span style="color:#98C379">                &#x3C;h2 class="login-title">用户登录&#x3C;/h2></span></span>
<span class="line"><span style="color:#98C379">                </span></span>
<span class="line"><span style="color:#98C379">                &#x3C;div class="form-group"></span></span>
<span class="line"><span style="color:#98C379">                    &#x3C;label class="form-label" for="username">用户名&#x3C;/label></span></span>
<span class="line"><span style="color:#98C379">                    &#x3C;input type="text" id="username" class="form-input"></span></span>
<span class="line"><span style="color:#98C379">                    &#x3C;div id="username-error" class="error-message">请输入用户名&#x3C;/div></span></span>
<span class="line"><span style="color:#98C379">                &#x3C;/div></span></span>
<span class="line"><span style="color:#98C379">                </span></span>
<span class="line"><span style="color:#98C379">                &#x3C;div class="form-group"></span></span>
<span class="line"><span style="color:#98C379">                    &#x3C;label class="form-label" for="password">密码&#x3C;/label></span></span>
<span class="line"><span style="color:#98C379">                    &#x3C;input type="password" id="password" class="form-input"></span></span>
<span class="line"><span style="color:#98C379">                    &#x3C;div id="password-error" class="error-message">请输入密码&#x3C;/div></span></span>
<span class="line"><span style="color:#98C379">                &#x3C;/div></span></span>
<span class="line"><span style="color:#98C379">                </span></span>
<span class="line"><span style="color:#98C379">                &#x3C;button id="login-btn" class="login-button">登录&#x3C;/button></span></span>
<span class="line"><span style="color:#98C379">                </span></span>
<span class="line"><span style="color:#98C379">                &#x3C;div id="result-message" class="result-message">&#x3C;/div></span></span>
<span class="line"><span style="color:#98C379">            &#x3C;/div></span></span>
<span class="line"><span style="color:#98C379">        `</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 元素引用</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">usernameInput</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> null</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">passwordInput</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> null</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">loginBtn</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> null</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">usernameError</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> null</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">passwordError</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> null</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">resultMessage</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> null</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 使用MutationObserver确保DOM完全加载后再访问元素</span></span>
<span class="line"><span style="color:#C678DD">        const</span><span style="color:#E5C07B"> observer</span><span style="color:#56B6C2"> =</span><span style="color:#C678DD"> new</span><span style="color:#61AFEF"> MutationObserver</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">mutations</span><span style="color:#C678DD"> =></span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">initializeElements</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#E5C07B">            observer</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">disconnect</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#ABB2BF">        });</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#E5C07B">        observer</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">observe</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">shadowRoot</span><span style="color:#ABB2BF">, {</span></span>
<span class="line"><span style="color:#E06C75">            childList</span><span style="color:#ABB2BF">: </span><span style="color:#D19A66">true</span><span style="color:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75">            subtree</span><span style="color:#ABB2BF">: </span><span style="color:#D19A66">true</span></span>
<span class="line"><span style="color:#ABB2BF">        });</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 初始化元素</span></span>
<span class="line"><span style="color:#61AFEF">    initializeElements</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">usernameInput</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">shadowRoot</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementById</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'username'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">passwordInput</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">shadowRoot</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementById</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'password'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">loginBtn</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">shadowRoot</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementById</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'login-btn'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">usernameError</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">shadowRoot</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementById</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'username-error'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">passwordError</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">shadowRoot</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementById</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'password-error'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">resultMessage</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">shadowRoot</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementById</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'result-message'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 添加点击事件</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">loginBtn</span><span style="color:#ABB2BF">) </span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">loginBtn</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">addEventListener</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'click'</span><span style="color:#ABB2BF">, () </span><span style="color:#C678DD">=></span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">handleLogin</span><span style="color:#ABB2BF">());</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 初始化输入框值</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">_updateInputValues</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 定义观察的属性</span></span>
<span class="line"><span style="color:#C678DD">    static</span><span style="color:#C678DD"> get</span><span style="color:#61AFEF"> observedAttributes</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD">        return</span><span style="color:#ABB2BF"> [</span><span style="color:#98C379">'login-url'</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'username-value'</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'password-value'</span><span style="color:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 监听属性变化</span></span>
<span class="line"><span style="color:#61AFEF">    attributeChangedCallback</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">name</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75;font-style:italic">oldValue</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75;font-style:italic">newValue</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#E06C75">name</span><span style="color:#56B6C2"> ===</span><span style="color:#98C379"> 'username-value'</span><span style="color:#56B6C2"> ||</span><span style="color:#E06C75"> name</span><span style="color:#56B6C2"> ===</span><span style="color:#98C379"> 'password-value'</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">_updateInputValues</span><span style="color:#ABB2BF">(); </span><span style="color:#7F848E;font-style:italic">// 响应式更新</span></span>
<span class="line"><span style="color:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 更新输入框值</span></span>
<span class="line"><span style="color:#61AFEF">    _updateInputValues</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">		/**</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">		 * 更新用户名</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">		 */</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">usernameInput</span><span style="color:#ABB2BF">) </span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">usernameInput</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">value</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">usernameValue</span><span style="color:#56B6C2"> ||</span><span style="color:#98C379"> ''</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">		/**</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">		 * 更新密码</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">		 */</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">passwordInput</span><span style="color:#ABB2BF">) </span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">passwordInput</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">value</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">passwordValue</span><span style="color:#56B6C2"> ||</span><span style="color:#98C379"> ''</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 获取/设置用户名值</span></span>
<span class="line"><span style="color:#C678DD">    get</span><span style="color:#61AFEF"> usernameValue</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD">        return</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getAttribute</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'username-value'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#C678DD">    set</span><span style="color:#61AFEF"> usernameValue</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">value</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#E06C75">value</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">setAttribute</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'username-value'</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">value</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">        } </span><span style="color:#C678DD">else</span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">removeAttribute</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'username-value'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">_updateInputValues</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 获取/设置密码值</span></span>
<span class="line"><span style="color:#C678DD">    get</span><span style="color:#61AFEF"> passwordValue</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD">        return</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getAttribute</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'password-value'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#C678DD">    set</span><span style="color:#61AFEF"> passwordValue</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">value</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#E06C75">value</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">setAttribute</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'password-value'</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">value</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">        } </span><span style="color:#C678DD">else</span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">removeAttribute</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'password-value'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">_updateInputValues</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 获取登录URL</span></span>
<span class="line"><span style="color:#C678DD">    get</span><span style="color:#61AFEF"> loginUrl</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD">        return</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getAttribute</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'login-url'</span><span style="color:#ABB2BF">) </span><span style="color:#56B6C2">||</span><span style="color:#98C379"> '#'</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 处理登录逻辑</span></span>
<span class="line"><span style="color:#61AFEF">    handleLogin</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 检查元素是否存在</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#56B6C2">!</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">usernameInput</span><span style="color:#56B6C2"> ||</span><span style="color:#56B6C2"> !</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">passwordInput</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">            console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">error</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'表单元素未正确初始化'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#C678DD">            return</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#C678DD">        const</span><span style="color:#E5C07B"> username</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">usernameInput</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">value</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">trim</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD">        const</span><span style="color:#E5C07B"> password</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">passwordInput</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">value</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">trim</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 简单验证</span></span>
<span class="line"><span style="color:#C678DD">        let</span><span style="color:#E06C75"> isValid</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> true</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">toggleError</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">usernameError</span><span style="color:#ABB2BF">, </span><span style="color:#56B6C2">!</span><span style="color:#E06C75">username</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">toggleError</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">passwordError</span><span style="color:#ABB2BF">, </span><span style="color:#56B6C2">!</span><span style="color:#E06C75">password</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E06C75">        isValid</span><span style="color:#56B6C2"> =</span><span style="color:#56B6C2"> !!</span><span style="color:#E06C75">username</span><span style="color:#56B6C2"> &#x26;&#x26;</span><span style="color:#56B6C2"> !!</span><span style="color:#E06C75">password</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#56B6C2">!</span><span style="color:#E06C75">isValid</span><span style="color:#ABB2BF">) </span><span style="color:#C678DD">return</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 准备表单数据</span></span>
<span class="line"><span style="color:#C678DD">        const</span><span style="color:#E5C07B"> formData</span><span style="color:#56B6C2"> =</span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75">            username</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">username</span><span style="color:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75">            password</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">password</span></span>
<span class="line"><span style="color:#ABB2BF">        };</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 禁用按钮并显示加载状态</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">loginBtn</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">loginBtn</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">disabled</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> true</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">loginBtn</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">textContent</span><span style="color:#56B6C2"> =</span><span style="color:#98C379"> '登录中...'</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF">        </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 模拟API调用</span></span>
<span class="line"><span style="color:#E5C07B">        this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">simulateApiCall</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">formData</span><span style="color:#ABB2BF">)</span></span>
<span class="line"><span style="color:#ABB2BF">            .</span><span style="color:#61AFEF">then</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">response</span><span style="color:#C678DD"> =></span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">                // 显示成功消息</span></span>
<span class="line"><span style="color:#E5C07B">                this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">showResult</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'登录成功！正在处理...'</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'success'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">                </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">                // 创建新的事件实例并设置detail</span></span>
<span class="line"><span style="color:#C678DD">                const</span><span style="color:#E5C07B"> loginEvent</span><span style="color:#56B6C2"> =</span><span style="color:#C678DD"> new</span><span style="color:#61AFEF"> CustomEvent</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'login'</span><span style="color:#ABB2BF">, {</span></span>
<span class="line"><span style="color:#E06C75">                    detail</span><span style="color:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#E06C75">                        formData</span><span style="color:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75">                        response</span></span>
<span class="line"><span style="color:#ABB2BF">                    },</span></span>
<span class="line"><span style="color:#E06C75">                    bubbles</span><span style="color:#ABB2BF">: </span><span style="color:#D19A66">true</span><span style="color:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75">                    composed</span><span style="color:#ABB2BF">: </span><span style="color:#D19A66">true</span></span>
<span class="line"><span style="color:#ABB2BF">                });</span></span>
<span class="line"><span style="color:#ABB2BF">                </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">                // 触发事件</span></span>
<span class="line"><span style="color:#E5C07B">                this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">dispatchEvent</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">loginEvent</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">                </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">                // 2秒后从DOM中移除</span></span>
<span class="line"><span style="color:#61AFEF">                setTimeout</span><span style="color:#ABB2BF">(() </span><span style="color:#C678DD">=></span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E5C07B">                    this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">remove</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#ABB2BF">                }, </span><span style="color:#D19A66">2000</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">            })</span></span>
<span class="line"><span style="color:#ABB2BF">            .</span><span style="color:#61AFEF">catch</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">error</span><span style="color:#C678DD"> =></span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">                // 显示错误消息</span></span>
<span class="line"><span style="color:#E5C07B">                this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">showResult</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'登录失败：'</span><span style="color:#56B6C2"> +</span><span style="color:#E5C07B"> error</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">message</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'error'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">            })</span></span>
<span class="line"><span style="color:#ABB2BF">            .</span><span style="color:#61AFEF">finally</span><span style="color:#ABB2BF">(() </span><span style="color:#C678DD">=></span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">                // 恢复按钮状态</span></span>
<span class="line"><span style="color:#C678DD">                if</span><span style="color:#ABB2BF"> (</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">loginBtn</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">                    this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">loginBtn</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">disabled</span><span style="color:#56B6C2"> =</span><span style="color:#D19A66"> false</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B">                    this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">loginBtn</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">textContent</span><span style="color:#56B6C2"> =</span><span style="color:#98C379"> '登录'</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">                }</span></span>
<span class="line"><span style="color:#ABB2BF">            });</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 显示或隐藏错误消息</span></span>
<span class="line"><span style="color:#61AFEF">    toggleError</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">element</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75;font-style:italic">show</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#E06C75">element</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">            element</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">style</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">display</span><span style="color:#56B6C2"> =</span><span style="color:#E06C75"> show</span><span style="color:#C678DD"> ?</span><span style="color:#98C379"> 'block'</span><span style="color:#C678DD"> :</span><span style="color:#98C379"> 'none'</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 显示结果消息</span></span>
<span class="line"><span style="color:#61AFEF">    showResult</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">message</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75;font-style:italic">type</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">resultMessage</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">resultMessage</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">textContent</span><span style="color:#56B6C2"> =</span><span style="color:#E06C75"> message</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">resultMessage</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">className</span><span style="color:#56B6C2"> =</span><span style="color:#ABB2BF"> </span></span>
<span class="line"><span style="color:#98C379">                `result-message </span><span style="color:#C678DD">${</span><span style="color:#E06C75">type</span><span style="color:#56B6C2"> ===</span><span style="color:#98C379"> 'success'</span><span style="color:#C678DD"> ?</span><span style="color:#98C379"> 'success-message'</span><span style="color:#C678DD"> :</span><span style="color:#98C379"> 'error-message-display'</span><span style="color:#C678DD">}</span><span style="color:#98C379">`</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">resultMessage</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">style</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">display</span><span style="color:#56B6C2"> =</span><span style="color:#98C379"> 'block'</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">            </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">            // 3秒后隐藏消息</span></span>
<span class="line"><span style="color:#61AFEF">            setTimeout</span><span style="color:#ABB2BF">(() </span><span style="color:#C678DD">=></span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD">                if</span><span style="color:#ABB2BF"> (</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">resultMessage</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">                    this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">resultMessage</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">style</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">display</span><span style="color:#56B6C2"> =</span><span style="color:#98C379"> 'none'</span><span style="color:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF">                }</span></span>
<span class="line"><span style="color:#ABB2BF">            }, </span><span style="color:#D19A66">3000</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 模拟API调用</span></span>
<span class="line"><span style="color:#C678DD">    async</span><span style="color:#61AFEF"> simulateApiCall</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">formData</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD">        return</span><span style="color:#C678DD"> new</span><span style="color:#E5C07B"> Promise</span><span style="color:#ABB2BF">((</span><span style="color:#E06C75;font-style:italic">resolve</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75;font-style:italic">reject</span><span style="color:#ABB2BF">) </span><span style="color:#C678DD">=></span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF">            setTimeout</span><span style="color:#ABB2BF">(() </span><span style="color:#C678DD">=></span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">                // 模拟API响应</span></span>
<span class="line"><span style="color:#C678DD">                if</span><span style="color:#ABB2BF"> (</span><span style="color:#E5C07B">formData</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">username</span><span style="color:#56B6C2"> ===</span><span style="color:#98C379"> 'admin'</span><span style="color:#56B6C2"> &#x26;&#x26;</span><span style="color:#E5C07B"> formData</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">password</span><span style="color:#56B6C2"> ===</span><span style="color:#98C379"> 'admin'</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#61AFEF">                    resolve</span><span style="color:#ABB2BF">({</span></span>
<span class="line"><span style="color:#E06C75">                        code</span><span style="color:#ABB2BF">: </span><span style="color:#D19A66">200</span><span style="color:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75">                        message</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">'登录成功'</span><span style="color:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75">                        token</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">'fake_token_123456'</span></span>
<span class="line"><span style="color:#ABB2BF">                    });</span></span>
<span class="line"><span style="color:#ABB2BF">                } </span><span style="color:#C678DD">else</span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF">                    reject</span><span style="color:#ABB2BF">(</span><span style="color:#C678DD">new</span><span style="color:#61AFEF"> Error</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'用户名或密码错误'</span><span style="color:#ABB2BF">));</span></span>
<span class="line"><span style="color:#ABB2BF">                }</span></span>
<span class="line"><span style="color:#ABB2BF">            }, </span><span style="color:#D19A66">1500</span><span style="color:#ABB2BF">); </span><span style="color:#7F848E;font-style:italic">// 模拟1.5秒的网络延迟</span></span>
<span class="line"><span style="color:#ABB2BF">        });</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 当组件首次被插入文档DOM时被调用</span></span>
<span class="line"><span style="color:#61AFEF">    connectedCallback</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 延迟执行确保DOM完全加载</span></span>
<span class="line"><span style="color:#61AFEF">        requestAnimationFrame</span><span style="color:#ABB2BF">(() </span><span style="color:#C678DD">=></span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD">            if</span><span style="color:#ABB2BF"> (</span><span style="color:#56B6C2">!</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">usernameInput</span><span style="color:#56B6C2"> ||</span><span style="color:#56B6C2"> !</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">passwordInput</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">                this</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">initializeElements</span><span style="color:#ABB2BF">();</span></span>
<span class="line"><span style="color:#ABB2BF">            }</span></span>
<span class="line"><span style="color:#ABB2BF">        });</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 当组件从文档DOM中删除时被调用</span></span>
<span class="line"><span style="color:#61AFEF">    disconnectedCallback</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 清理事件监听器</span></span>
<span class="line"><span style="color:#C678DD">        if</span><span style="color:#ABB2BF"> (</span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">loginBtn</span><span style="color:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B">            this</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">loginBtn</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">removeEventListener</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'click'</span><span style="color:#ABB2BF">, </span><span style="color:#E5C07B">this</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">handleLogin</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">    </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">    // 当组件被移动到新的文档时被调用</span></span>
<span class="line"><span style="color:#61AFEF">    adoptedCallback</span><span style="color:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic">        // 处理组件被移动的逻辑</span></span>
<span class="line"><span style="color:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div><div class="collapsed-lines"></div></div><p>main.js（组件注册）</p>
<div class="language-javascript line-numbers-mode" data-highlighter="shiki" data-ext="javascript" style="background-color:#282c34;color:#abb2bf"><pre class="shiki one-dark-pro vp-code" v-pre=""><code><span class="line"><span style="color:#C678DD">import</span><span style="color:#E06C75"> LoginFrame</span><span style="color:#C678DD"> from</span><span style="color:#98C379"> "./login-frame/index.js"</span><span style="color:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B">customElements</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">define</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'login-frame'</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">LoginFrame</span><span style="color:#ABB2BF">); </span><span style="color:#7F848E;font-style:italic">// 定义组件</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;font-style:italic">// 父容器监听登录事件</span></span>
<span class="line"><span style="color:#E5C07B">document</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">getElementById</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'app'</span><span style="color:#ABB2BF">).</span><span style="color:#61AFEF">addEventListener</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'login'</span><span style="color:#ABB2BF">, (</span><span style="color:#E06C75;font-style:italic">event</span><span style="color:#ABB2BF">) </span><span style="color:#C678DD">=></span><span style="color:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E5C07B">	console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">log</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'接收到登录事件:'</span><span style="color:#ABB2BF">, </span><span style="color:#E5C07B">event</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">detail</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B">	console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">log</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'将从DOM中移除登录表单'</span><span style="color:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF">});</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>参考：</p>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components" target="_blank" rel="noopener noreferrer">Web Component - Web API | MDN</a></p>
<p><a href="https://blog.csdn.net/Naisu_kun/article/details/120305587" target="_blank" rel="noopener noreferrer">前端笔记：使用Web Components进行原生组件化开发_es6-string-html-CSDN博客</a></p>
</blockquote>
</div></template>


